/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ChateoController;

import java.net.*;
import java.util.ArrayList;
import ChateoFacadeNI.*;
import ChateoFacadeGUI.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author abdala
 * @author tchordjallian
 */
public class Controller {
    
    //The list of user where each user is defined by an id you can find in its own class
    private UsersList userList;
    //The list of messages where each message is defined by an id you can find in its own class
    private ArrayList<ChatMessage> messageList = new ArrayList<ChatMessage>();
    //The debugging state will provide some more verbose
    private static boolean debug=false;
    //Login of the local user
    private String myLogin = "toto";
    
    
    //IDs for a user
    InetAddress addr;
    String login;
    //The next two Won't work , will never receive the true login and addr
    User LoggedInUser = new User(addr,login, true);
    User LoggedOutUser = new User(addr,login, false);
    
    //Facades
    private FacadeGUI facadegui;
    private FacadeNI facadeni;
    //Port : STATIC 
    private static int port=2014;
    //GUI : STATIC
    private static boolean gui;
    
    public Controller(){
        //TODO : WAITING FOR GUI This is the version that will generate the whole system
        this.facadegui = new FacadeGUI(this);
    }
    public Controller(String login, int port) throws SocketException, NullPointerException, UnknownHostException{
        //This is the text version of the Controller
        this.myLogin=login;
        this.port=port;
        facadeni= new FacadeNI(this,port);
        gui=false;
        
        
    }
    
     public Controller(int port) throws SocketException, NullPointerException, UnknownHostException{

        //This is the text version of the Controller

        this.port=port;
        gui=true;

        //facadeni= new FacadeNI(this,port);


    }
    /**
     * 
     * @param remoteLogin
     * @param remoteAddr
     * @return
     * @throws UnknownHostException 
     */
    public String newUser(String remoteLogin,InetAddress remoteAddr) throws UnknownHostException{
        String out = "";
        User theNewOne=new User(remoteAddr, remoteLogin, true);
        LoggedInUser.setAddr(remoteAddr);
        LoggedInUser.setLogin(remoteLogin);
        LoggedOutUser.setAddr(remoteAddr);
        LoggedOutUser.setLogin(remoteLogin);
        
        //We check if the user doesn't already exist in our user list.
        //Ne pas créer un user tant qu'on n'a pas vérifié s'il existe ou pas
        if (!theNewOne.equals(userList.getLocalUser())){
        if(userList.contains(LoggedInUser)){
            System.err.println("The user " +LoggedInUser.getLogin()+ " already exists in the list.Please enter a new login");
            //System.exit(-1);
            //Avec un system exit le programme du serveur de l'autre côté crashe ce n'est probablement pas la meilleure solution 
            //je pencherai probablement plus sur un return
            //return("Error existing user in list");
            
        }
        else if(userList.contains(LoggedOutUser)){
            //We create a new user
            userList.get(userList.indexOf(LoggedOutUser)).setLoggingState(true);
        }
        else{
            
               
        //Now, the goal is to add him to the list
        userList.add(theNewOne);
        if (isGUI()){ updateGUI(); }
        }
        try {
            //userList.add(theNewOne.getId(),theNewOne);
            //Send the ACK to the user that just got connected with our ID
            sendAck(myLogin,InetAddress.getByName("255.255.255.255"));
        } catch (SocketException ex) {
            Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
        }
        //GUI Update
        // TODO WHEN THE GUI WILL BE READY
        //
        
        if (DebugMode()){
            //System.out.println("User: " +userList.get(userList.indexOf(LoggedOutUser)) + "has been added to the user List");
            out=remoteLogin;
        }
        else{
            out=remoteLogin;
        }
        return out;
        }
        return myLogin;
        
    }

    public String newUserAck(String remoteLogin,InetAddress remoteAddr)throws UnknownHostException{
        String out = "";
        User theNewOne=new User(remoteAddr, remoteLogin, true);
        LoggedInUser.setAddr(remoteAddr);
        LoggedInUser.setLogin(remoteLogin);
        LoggedOutUser.setAddr(remoteAddr);
        LoggedOutUser.setLogin(remoteLogin);
        if (!theNewOne.equals(userList.getLocalUser())){
        //We check if the user doesn't already exist in our user list.
        //Ne pas créer un user tant qu'on n'a pas vérifié s'il existe ou pas
        if(userList.contains(LoggedInUser)){
            System.err.println("The user " +LoggedInUser.getLogin()+ " already exists in the list.Please enter a new login");
            //System.exit(-1);
            //Avec un system exit le programme du serveur de l'autre côté crashe ce n'est probablement pas la meilleure solution 
            //je pencherai probablement plus sur un return
            return("Error existing user in list");
            
        }
        else if(userList.contains(LoggedOutUser)){
            //We create a new user
            userList.get(userList.indexOf(LoggedOutUser)).setLoggingState(true);
            System.out.println("Welcome back "+remoteLogin);
            theNewOne = new User(remoteAddr, remoteLogin);
        }
        else        
        //Now, the goal is to add him to the listl
        userList.add(theNewOne);
        if (isGUI()){ updateGUI(); }
        
        
        //GUI Update
        // TODO WHEN THE GUI WILL BE READY
        //
        
        if (DebugMode()){
            System.out.println("User: " +userList.get(userList.indexOf(theNewOne)) + "has been added to the user List");
            out=remoteLogin;
        }
        else{
            out=remoteLogin;
        }
        return out;
        }
        else {
            return myLogin;
        }
        
    }
    
    public void sendAck(String login,InetAddress IPaddr) throws UnknownHostException, SocketException{
        //TODO : 
        // 
        //-Lancer la methode de la facade qui envoie le ACK
        facadeni.outgoingAck(IPaddr, login);
        //
    }
    
    //This method is to check if a user has just left
    
   //public String userLeft(String login, InetAddress IPaddr){
    public boolean userLeft(String remoteLogin,InetAddress remoteAddr ){
        //String out="";
        int slot=-1;
        //TODO : 
        //-Utiliser la recherche dans userlist par Login et IP
        User LeavingUser=new User(remoteAddr,remoteLogin,true);
        if (debug)System.out.println("User "+remoteLogin+" with the address "+remoteAddr+" tries to leave");
         
              //-Changer le status de l'user
              if (userList.contains(LeavingUser)){
                  slot=userList.indexOf(LeavingUser);
            userList.get(slot).setLoggingState(false);
            System.out.println("The user " +LeavingUser.getLogin()+ " has left");
              }
              else
              {
                  System.out.println("The user " +remoteLogin+ " with the ip "+remoteAddr+" has left but we can't find him");
                  System.out.println("These are the current users : "+userList.toString());
              }
       if (isGUI()){ updateGUI(); }
        //-Effectuer la MAJ du GUI
        //return out;
          return true;
        
    }
    
    /**
     * This method is aimed at receiving messages
     */
    
    public void newTextMessage(String remoteLogin,InetAddress remoteAddr,String text,boolean privato){
        User me=userList.getLocalUser();
        User remoteUser=new User(remoteAddr, remoteLogin, true);
        if (userList.contains(remoteUser)){
            
            ChatMessage currentMessage = new ChatMessage(text, remoteUser, me, privato);
            messageList.add(currentMessage);
            System.out.println(currentMessage);
        }
        else
        {
            if (debug) {System.out.println("Messsage Received from an unknown user : "+ remoteLogin +" : \n"+text);}
        }
        if (isGUI()){ updateGUI(); }
                
    }
    
     /**
     * This method is aimed at Sending messages
     * 
     * 
    */
    
    public void sendMessage(User remoteUser,String text,boolean privato) throws SocketException, UnknownHostException{
        if (gui){
            remoteUser=userList.get(remoteUser);
        }
        User me=userList.getLocalUser();
        if (privato){
            
       
            facadeni.outgoingText(remoteUser.getAddr(), me.getLogin(), text, privato);
            ChatMessage currentMessage = new ChatMessage(text, me, remoteUser, privato);
            remoteUser.addMessage(currentMessage);
            messageList.add(currentMessage);
            System.out.println(currentMessage);
        }
        else {
            facadeni.outgoingText(InetAddress.getByName("255.255.255.255"), me.getLogin(), text, privato);
            ChatMessage currentMessage = new ChatMessage(text, me, me, privato);
            me.addMessage(currentMessage);
            messageList.add(currentMessage);
            System.out.println(currentMessage);
        }
        if (isGUI()){ updateGUI(); }
                
    }
    
    
    /*
     * This Method is aimed at Testing the message sending method.
     */
    public void mensajesBasicos(String mensaje, String login, boolean privato) throws SocketException, UnknownHostException{
        User remoteUser=userList.getUserByLogin(login);
        sendMessage(remoteUser,mensaje,privato);
    }
    
    /**
     * This method is aimed at enabling to connect a localUser.
     * @throws java.net.SocketException
     * @throws java.net.UnknownHostException
    */
    
    public void connect() throws NullPointerException, SocketException, UnknownHostException {
        try{
            userList= new UsersList(new User(InetAddress.getLocalHost(),myLogin, true));
            facadeni.outgoingHello(InetAddress.getByName("255.255.255.255"),myLogin);
            System.out.println("The user " +myLogin+ " has been connected \n");
        }
        catch(SocketException se){
            System.out.println("Error in creation");
        }
        
	
    }
    public void connectFromGUI(String login) throws NullPointerException, SocketException, UnknownHostException {
        this.myLogin=login;
        this.port=port;
        //facadeni= new FacadeNI(this,port);
        try{
            userList= new UsersList(new User(InetAddress.getLocalHost(),myLogin, true));
            facadeni.outgoingHello(InetAddress.getByName("255.255.255.255"),myLogin);
            System.out.println("The user " +myLogin+ " has been connected \n");
            //if (isGUI()){ updateGUI(); }
        }
        catch(SocketException se){
            System.out.println("Error in creation");
        }
        
	
    }
    
    /**
     * This method is aimed at enabling to disconnect a localUser.
     * @throws java.net.UnknownHostException
     * @throws java.net.SocketException
    */
    public void disconnect() throws UnknownHostException, SocketException {
        facadeni.outgoingBye(InetAddress.getByName("255.255.255.255"),myLogin);
        System.out.println("Disconnection of the user "+myLogin);
        clearData();
        if (isGUI()){ updateGUI(); }
    }

    public UsersList getUserList() {
        return userList;
    }
    
    public String getMyLogin() {
        return myLogin;
    }
    
    public static boolean DebugMode() {
        return debug;
    }
    
    public User getUserByLogin(String login) {
        System.out.println("getuserbylogin");
        User user2Return = null;
        int size = this.userList.size();
        System.out.println("Found: "+size);
        for (int i = 0; i < size; i++) {

            if (userList.get(i).getLogin().equals(login)) {
                user2Return = userList.get(i);
                System.out.println("Found: " +user2Return);
                break;
            } 
        }
        return user2Return;
    }

    public void setFacadegui(FacadeGUI facadegui) {
        this.facadegui = facadegui;
    }

    public void setFacadeni(FacadeNI facadeni) {
        this.facadeni = facadeni;
    }
    public static boolean isGUI(){
    return gui ;
    }
    
    public void updateGUI(){
        ArrayList<ChatMessage> chatMessages= messageList;
        facadegui.updateGUI(userList.getConnectedUsers(),messageList);
    }
    public void clearData(){
        messageList=new ArrayList<ChatMessage>();
        userList.clear();
    }


}
